﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IGolovkine.ShoutCastRipper.Service.Configuration;
using IGolovkine.ShoutCastRipper.Filters;
using IGolovkine.Core;

namespace IGolovkine.ShoutCastRipper.Service
{
    public class StationRipperService : IStationRipperService
    {
        private readonly ILogWriter _logWriter;
        private readonly TypeFactory<IStationConnection> _connectionFactory;
        private readonly TypeFactory<IStationClient> _clientFactory;
        private readonly IContentTitleProbingPolicy _probingPolicy;
        private readonly IContentRepository _contentRepository;
        private readonly IContentLengthPolicy _lengthPolicy;
        private readonly IStationRipper _ripper;
        private readonly string _name;

        private readonly Dictionary<string, IStationRipperService> _backUpServices;

        public StationRipperService(StationRipperElement p)
        {
            _name = p.Name;
            _logWriter = this.CreateStationLogWriter(p);
            _connectionFactory = this.CreateStationConnectionFactory(p);
            _clientFactory = this.CreateStationClientFactory(p, _connectionFactory, _logWriter);
            _probingPolicy = this.CreateContentTitleProbingPolicy(p, _logWriter);
            _contentRepository = this.CreateContentRepository(p, _logWriter);
            _lengthPolicy = this.CreateContentLengthPolicy(p, _logWriter);
            _ripper = this.CreateStationRipper(p, _clientFactory, _contentRepository, _probingPolicy, _lengthPolicy, _logWriter);

            _logWriter.WriteLine("Created {0}", p);
            _logWriter.WriteLine();
            _backUpServices = new Dictionary<string,IStationRipperService>();
        }

        public void AttachBackUp(IStationRipperService backUpService)
        {
            if (!object.ReferenceEquals(this, backUpService))
            {
                _backUpServices.Add(backUpService.Name, backUpService);
            }
            else
            {
                throw new InvalidOperationException(string.Format("'{0}' cannot be attached as a back up to itself", _name));
            }
        }

        public IStationRipper Ripper
        {
            get { return _ripper; }
        }

        public string Name
        {
            get { return this._name; }
        }

        private void OnPrimaryInstanceDisconnected()
        {
            foreach (IStationRipperService backUpInstance in this._backUpServices.Values)
            {
                if (!backUpInstance.Ripper.IsStarted)
                {
                    backUpInstance.Ripper.Start();
                }
            }
        }

        private void OnPrimaryInstanceConnected()
        {
            foreach (IStationRipperService backUpInstance in this._backUpServices.Values)
            {
                if (backUpInstance.Ripper.IsConnected)
                {
                    backUpInstance.Ripper.StopAfterCurrentTitle();
                }
            }
        }

        private void OnBackUpInstanceConnected(IStationRipperService connectedBackUpInstance)
        {
            foreach (IStationRipperService backUpInstance in _backUpServices.Values)
            {
                if (object.ReferenceEquals(connectedBackUpInstance, backUpInstance) == false)
                {
                    if (backUpInstance.Ripper.IsStarted)
                    {
                        backUpInstance.Ripper.Stop();
                    }

                }
            }
        }

        private void BackUpInstanceDisconnected(IStationRipperService disconnectedBackUpInstance)
        {
            foreach (IStationRipperService backUpInstance in _backUpServices.Values)
            {
                if (object.ReferenceEquals(disconnectedBackUpInstance, backUpInstance) == false)
                {
                    if (!backUpInstance.Ripper.IsStarted)
                    {
                        backUpInstance.Ripper.Start();
                    }
                }
            }
        }

        protected virtual ILogWriter CreateStationLogWriter(StationRipperElement p)
        {
            CompositeLogWriter logWriter = new CompositeLogWriter();
            logWriter.LogWriters.Add(new ConsoleLogWriter(p.Name));
            logWriter.LogWriters.Add(new FileLogWriter(p.LoggerParameter));
            return logWriter;
        }

        protected virtual TypeFactory<IStationConnection> CreateStationConnectionFactory(StationRipperElement p)
        {
            return () => new StationConnection(p.Url, p.UserAgent);
        }

        protected virtual TypeFactory<IStationClient> CreateStationClientFactory(StationRipperElement p, TypeFactory<IStationConnection> connectionFactory, ILogWriter logWriter)
        {
            return () => new StationClient(() => new StationConnection(p.Url, p.UserAgent));
        }

        protected virtual IContentTitleProbingPolicy CreateContentTitleProbingPolicy(StationRipperElement p, ILogWriter logWriter)
        {
            return new ContentTitleProbingPolicy(p.MinimumProbingPeriod, p.MaximumProbingPeriod, logWriter);
        }

        protected virtual IContentRepository CreateContentRepository(StationRipperElement p, ILogWriter logWriter)
        {
            return new ContentFileRepository(p.StagingFolder, new string[] { p.ArchiveFolder });
        }

        protected virtual IContentLengthPolicy CreateContentLengthPolicy(StationRipperElement p, ILogWriter logWriter)
        {
            return new ContentLengthPolicy(p.MinimumTitleLength, p.MaximumTitleLength);
        }

        protected virtual IStationRipper CreateStationRipper(StationRipperElement p, TypeFactory<IStationClient> stationClientFactory, IContentRepository contentRepository,
           IContentTitleProbingPolicy contentProbingPolicy, IContentLengthPolicy lengthPolicy, ILogWriter logWriter)
        {
            StationRipper stationRipper = new StationRipper(p.Name, stationClientFactory, contentRepository, contentProbingPolicy, lengthPolicy, logWriter);
            IContentTitleDateExtractor dataExtractor = new ContentTitleDateExtractor(p.DatePatterns.ConvertAll(e => e.Regex));
            stationRipper.TitleFilters.Add(new ContentTitleAgeFilter(p.MaximumRecordingAge, dataExtractor));
            stationRipper.TitleFilters.Add(new ContentTitlePatternFilter(p.StreamTitleFilters));
            stationRipper.TitleFilters.Add(new ContentTitleDuplicatesFilter(contentRepository));
            return stationRipper;
        }

        public virtual void Dispose()
        {
            _ripper.Dispose();
            _contentRepository.Dispose();
        }
    }
}
